package com.yt.server.io.amf3;

import java.util.zip.InflaterInputStream;
import java.io.DataInputStream;
import java.io.UnsupportedEncodingException;
import flex.messaging.io.SerializationContext;
import flex.messaging.io.amf.Amf3Input;
import flex.messaging.io.amf.ASObject;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.AttributeKey;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import com.yt.server.messaging.Packet;

/**
 * AMF3 解码器
 * 
 * @author LiuJ
 */
public class AMF3Decoder extends CumulativeProtocolDecoder {

	private final AttributeKey POLICY = new AttributeKey(getClass(), "Policy");

	private final AttributeKey AMF3INPUTKEY = new AttributeKey(getClass(),
			"Amf3Input");
	private final AttributeKey ISPOSITION = new AttributeKey(getClass(),
			"IsPosition");

	private final String security = "<policy-file-request/>";

	private final SerializationContext context = new SerializationContext();

	public AMF3Decoder() {
	}

	protected boolean doDecode(IoSession session, IoBuffer in,
			ProtocolDecoderOutput out) throws Exception {
		IoBuffer buffer = null;
		Amf3Input amf3in = null;
		String sid = String.valueOf(session.getId());
		if (isSecurityRequest(session, in)) {
			out.write(security);
			in.free();
			return false;
		} else {
			try {
				int len = 0;
				if (null == session.getAttribute(ISPOSITION)) {
					in.position(0);
					session.setAttribute(ISPOSITION, true);
				}
				if (in.prefixedDataAvailable(4)) {
					in.mark();
					// 上次包是粘包
					if (in.remaining() > 4) {
						byte[] buf = new byte[4];
						int value = 0;
						in.get(buf);
						for (int i = 0; i < buf.length; i++) {
							len <<= 0;
							value = buf[i] & 0xff;
							len |= value;
						}
					} else
						return false;
					// 是否数据不完整
					if (len > in.remaining()) {
						in.reset();
						return false;
					} else {
						// 开始读取消息内容
						Object message = null;
						byte bytes[] = new byte[len];
						in.get(bytes);
						buffer = IoBuffer.wrap(bytes);
						Object input = session.getAttribute(AMF3INPUTKEY);
						if (input != null) {
							amf3in = (Amf3Input) input;
						} else {
							amf3in = new Amf3Input(context);
							session.setAttribute(AMF3INPUTKEY, amf3in);
						}
						amf3in.setInputStream(new InflaterInputStream(
								new DataInputStream(buffer.asInputStream())));
						if (amf3in.available() > 0) {
							if ((null != (message = amf3in.readObject()))
									&& (message instanceof ASObject || message instanceof Packet)) {
								out.write(message);
								return true;
							}
						}
					}
				} else
					return false;
			} catch (Exception e) {
				System.out.println(sid + " : AMF3Decoder Exception " + e);
				e.printStackTrace();
				return false;
			} finally {
				if (amf3in != null)
					amf3in.reset();
				if (buffer != null)
					in.free();
				buffer = null;
			}
			return false;
		}
	}

	// public static int proxyLength = 0;
	// protected boolean doDecode(IoSession session, IoBuffer in,
	// ProtocolDecoderOutput out) throws Exception {
	// try {
	// if (session.getAttribute("firstTimeFilter") == null
	// && proxyLength > 0) {
	// if (in.limit() >= proxyLength) {
	// session.setAttribute("firstTimeFilter", true);
	// in.position(proxyLength);
	// if (in.remaining() < 4)
	// return false;
	// } else {
	// return false;
	// }
	// }
	// if (in.prefixedDataAvailable(4, 5000)) {
	// int length = in.getInt();
	// byte[] bytes = new byte[length];
	// in.get(bytes);
	// Object input = session.getAttribute("Af3Input");
	// Amf3Input amf3in = null;
	// if (input != null)
	// amf3in = (Amf3Input) input;
	// else {
	// amf3in = new Amf3Input(context);
	// session.setAttribute("Af3Input", amf3in);
	// }
	// amf3in.reset();
	// ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
	// amf3in.setInputStream(new InflaterInputStream(bais));
	// out.write(amf3in.readObject());
	// return true;
	// } else {
	// System.out.println("resume reveice Data :" + in.remaining());
	// return false;
	// }
	// } catch (Exception e) {
	// e.printStackTrace();
	// throw e;
	// }
	// }

	private boolean isSecurityRequest(IoSession session, IoBuffer in) {
		Boolean policy = (Boolean) session.getAttribute(POLICY);
		if (policy != null) {
			return false;
		}
		String request = getRequest(in);
		boolean result = false;
		if (request != null) {
			result = request.startsWith(security);
		}
		session.setAttribute(POLICY, new Boolean(result));
		return result;
	}

	private String getRequest(IoBuffer in) {
		byte[] bytes = new byte[in.limit()];
		in.get(bytes);
		String request;
		try {
			request = new String(bytes, "UTF-8");
		} catch (UnsupportedEncodingException e) {
			request = null;
		}
		return request;
	}
}